/*
 *  renderer.cpp
 *  
 *  Created by James Dykstra
 *	Team 5
 *
 *  Copyright 2010 Michigan Technological University. All rights reserved.
 */

#include "renderer.h"

/*Renderer()
/ Initializes SDL video and audio subsystems, and sets up the screen.
*/
Renderer::Renderer()
{
	//Outputs any errors to a log, output.txt
	std::fstream log("log.txt", std::fstream::out | std::fstream::trunc);
	log<<"Initializing SDL:        ";

	//Initializing SDL subsystems
	if(SDL_Init(SDL_INIT_VIDEO)==-1)
		log<<"ERROR: Failed to initialize SDL!\n";

	log<<"Done"<<std::endl<<"Creating OpenGL Context: ";

	//Establishing the screen at the specified resolution
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	screen = SDL_SetVideoMode(RES_WIDTH, RES_HEIGHT, SCREEN_BPP, SDL_OPENGL);
	if(screen == NULL)
		log<<"ERROR: Failed to initialize and create screen!\n";

	log<<"Done"<<std::endl<<"Setting Perspective:     ";

	//Enable textures and set clear color to black
	glEnable(GL_TEXTURE_2D);
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

	//Establish viewport and clear color buffer
	glViewport(0, 0, RES_WIDTH, RES_HEIGHT);
	glClear(GL_COLOR_BUFFER_BIT);

	//Setting the view
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glOrtho(0.0, RES_WIDTH, RES_HEIGHT, 0.0, -1.0, 1.0);

	//Set to modelview for drawing/texturing
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
    //turn on some sdl flags
  	SDL_ShowCursor(SDL_DISABLE);
  	SDL_EnableUNICODE(1);
  	CEGUI::OpenGLRenderer& gui = CEGUI::OpenGLRenderer::create();
  	CEGUI::System::create(gui);
  	guiDraw();
  	ingame=false;
  exit=false;
  isserver=true;

	log<<"Done\n";

	log.close();
}
/*~Renderer()
/ Cleans up all our valid pointers and shuts down the SDL subsystems.
*/
Renderer::~Renderer()
{
	if(screen!=NULL)
		SDL_FreeSurface(screen);

	SDL_Quit();
}
/*drawSurface()
/ Draws a given texture at the specified position.
/ pos - rect containing coordinates and size
/ angle - the angle in degrees that this image is facing
/ texture - the texture id of the image to be drawn
*/
void Renderer::drawSurface(SDL_Rect *pos, GLfloat angle, GLuint texture)
{
	glLoadIdentity();

	if(angle != 0.f)
	{
		//Calculate w/2 and h/2 in order to translate into the center of the object and rotate it about its center
		GLfloat wT = (GLfloat)pos->w/2;
		GLfloat hT = (GLfloat)pos->h/2;
		glTranslatef((GLfloat)pos->x + wT, (GLfloat)pos->y + hT, 0.f);
		glRotatef(angle, 0.f, 0.f, 1.0f);
		glTranslatef(-wT, -hT, 0.f);
	}
	else
		glTranslatef((GLfloat)pos->x, (GLfloat)pos->y, 0.f);

	glBindTexture(GL_TEXTURE_2D, texture);

	//Enabling alpha
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glBegin(GL_QUADS);
		glTexCoord2i(0, 0);
		glVertex3f(0.f, 0.f, 0.f);

		glTexCoord2i(1, 0);
		glVertex3f((GLfloat)pos->w, 0.f, 0.f);

		glTexCoord2i(1, 1);
		glVertex3f((GLfloat)pos->w, (GLfloat)pos->h, 0.f);

		glTexCoord2i(0, 1);
		glVertex3f(0.f, (GLfloat)pos->h, 0.f);
	glEnd();
}
/*loadImage()
/ Loads an image file as an OpenGL texture and returns the texture id or null on failure.
/ filename - The file path to the image file to be loaded.
*/
SDL_Surface* Renderer::loadImage(std::string filename)
{
	SDL_Surface *img    = NULL;
	SDL_Surface *optimg = NULL;

	img = IMG_Load(filename.c_str());
	//Optimizing the image into the proper display format
	if(img != NULL)
	{
		optimg = SDL_DisplayFormatAlpha(img);

		//Send the SDL_Surface off to be used to create an OpenGL texture and return the id
		if(optimg != NULL)
		{
			SDL_FreeSurface(img);
			return optimg;
		}
		else
			return img;
	}
	return NULL;
}
/*update()
/ Flips the buffer, to update screen.
/ For use by the Game Engine ONLY.
/ Must be called once per iteration of the game loop.
*/
void Renderer::update()
{
    CEGUI::System::getSingleton().renderGUI();
	SDL_GL_SwapBuffers();
	glClear(GL_COLOR_BUFFER_BIT);
}
/*getPixelAt()
/ Returns a pixel from the given SDL_Surface at the specified coordinates.
/ Used for per-pixel collision detection.
*/
Uint32 Renderer::getPixelAt(SDL_Surface *surface, int x, int y)
{
	//Calculating the pixel's representation
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * (surface->format->BytesPerPixel);

	//Returns the pixel in the proper format
    switch(surface->format->BytesPerPixel)
	{
    case 1:
        return *p;
		break;

    case 2:
        return *(Uint16 *)p;
		break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
            return p[0] << 16 | p[1] << 8 | p[2];
        else
            return p[0] | p[1] << 8 | p[2] << 16;
		break;

    case 4:
        return *(Uint32 *)p;
		break;

    default:
        return 0;
		break;
    }
}
//Method used to consolidate the code to create an OpenGL texture from an SDL_Surface.
GLuint Renderer::createTexture(SDL_Surface *surf)
{
	if(surf == NULL)
		return NULL;

	if(isPow2(surf->w) && isPow2(surf->h))
	{
		SDL_Surface *texSurf = SDL_CreateRGBSurface(SDL_SWSURFACE, surf->w, surf->h, 32, surf->format->Rmask, surf->format->Gmask, surf->format->Bmask, surf->format->Amask);

		SDL_SetAlpha(surf, 0, 0);
		SDL_SetAlpha(texSurf, 0, 0);
		SDL_BlitSurface(surf, 0, texSurf, 0);

		GLuint texBuf = NULL;
		GLenum textureFmt;
		GLint numColors;

		numColors = texSurf->format->BytesPerPixel;
		if(numColors == 4)
			if(texSurf->format->Rmask == 0x000000ff)
				textureFmt = GL_RGBA;
			else
				textureFmt = GL_BGRA;
		else if(numColors == 3)
			if(texSurf->format->Rmask == 0x000000ff)
				textureFmt = GL_RGB;
			else
				textureFmt = GL_BGR;
		else
			return texBuf;

		glGenTextures(1, &texBuf);
		glBindTexture(GL_TEXTURE_2D, texBuf);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		glTexImage2D(GL_TEXTURE_2D, 0, numColors, texSurf->w, texSurf->h, 0, textureFmt, GL_UNSIGNED_BYTE, texSurf->pixels);

		SDL_FreeSurface(texSurf);

		return texBuf;
	}
	else
	{
		std::fstream log("log.txt", std::fstream::out | std::fstream::app);
		log<<"SDL_Surface: "<<surf<<" does not have power of 2 dimensions!"<<std::endl;
		log<<"w: "<<surf->w<<" | h: "<<surf->h<<std::endl;
		log.close();

		return NULL;
	}
}

int Renderer::getScreenWidth(){
	return screen->w;
}
int Renderer::getScreenHeight(){
	return screen->h;
}

/**
 * guiDraw draws the gui; it also manages the state of the gui.
 */
void Renderer::guiDraw()
{
CEGUI::DefaultResourceProvider* rp = static_cast<CEGUI::DefaultResourceProvider*>
          (CEGUI::System::getSingleton().getResourceProvider());
  rp->setResourceGroupDirectory("schemes", "gui/datafiles/schemes/");
  rp->setResourceGroupDirectory("imagesets", "gui/datafiles/imagesets/");
  rp->setResourceGroupDirectory("fonts", "gui/datafiles/fonts/");
  rp->setResourceGroupDirectory("layouts", "./");
  rp->setResourceGroupDirectory("looknfeels", "gui/datafiles/looknfeel/");
  rp->setResourceGroupDirectory("lua_scripts", "gui/datafiles/lua_scripts/");
  CEGUI::Imageset::setDefaultResourceGroup("imagesets");
  CEGUI::Font::setDefaultResourceGroup("fonts");
  CEGUI::Scheme::setDefaultResourceGroup("schemes");
  CEGUI::WidgetLookManager::setDefaultResourceGroup("looknfeels");
  CEGUI::WindowManager::setDefaultResourceGroup("layouts");
  CEGUI::ScriptModule::setDefaultResourceGroup("lua_scripts");
  CEGUI::SchemeManager::getSingleton().create("VanillaSkin.scheme"); 
  CEGUI::SchemeManager::getSingleton().create("TaharezLook.scheme"); 
  CEGUI::System::getSingleton().setDefaultMouseCursor("Vanilla-Images", "MouseArrow");
  //load fonts, first one created is used as default
  CEGUI::FontManager::getSingleton().createFreeTypeFont("DefaultFont", 10/*pt*/, true, "DejaVuSans.ttf");
  CEGUI::FontManager::getSingleton().create("FairChar-30.font");
  CEGUI::FontManager::getSingleton().create("Batang-26.font");
  //create a root window for all other windows to sit on, and load it
  myRoot = CEGUI::WindowManager::getSingleton().createWindow( "DefaultWindow", "root" );

  CEGUI::System::getSingleton().setGUISheet( myRoot );

   myHUD = CEGUI::WindowManager::getSingleton().loadWindowLayout( "HUD.layout" );
   myHUD->setVisible(false);
   myRoot->addChildWindow(myHUD ); 
   myOptions = CEGUI::WindowManager::getSingleton().loadWindowLayout( "options.layout" );
   myOptions->setVisible(false);
   myRoot->addChildWindow( myOptions ); 

   myLoadout = CEGUI::WindowManager::getSingleton().loadWindowLayout( "loadout.layout" );
   myLoadout->setVisible(false);

   myRoot->addChildWindow( myLoadout ); 
  //load the root layout (main menu)
   myRoot = CEGUI::WindowManager::getSingleton().loadWindowLayout( "spaceshooter.layout" );
  CEGUI::System::getSingleton().setGUISheet( myRoot );

  //tie in the event handlers?
  CEGUI::WindowManager::getSingleton().getWindow("Options")->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&Renderer::handleButton, this));
  CEGUI::WindowManager::getSingleton().getWindow("Play")->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&Renderer::handleButton, this));
  CEGUI::WindowManager::getSingleton().getWindow("Launch")->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&Renderer::handleButton, this));
  CEGUI::WindowManager::getSingleton().getWindow("Backtomenu")->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&Renderer::handleButton, this));
  CEGUI::WindowManager::getSingleton().getWindow("LoadoutBacktomenu")->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&Renderer::handleButton, this));
  CEGUI::WindowManager::getSingleton().getWindow("Sound")->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&Renderer::handleButton, this));
  CEGUI::WindowManager::getSingleton().getWindow("Exit")->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&Renderer::handleButton, this));
  CEGUI::WindowManager::getSingleton().getWindow("serverchoice")->subscribeEvent(CEGUI::RadioButton::EventSelectStateChanged, CEGUI::Event::Subscriber(&Renderer::handleRadio, this));
  CEGUI::WindowManager::getSingleton().getWindow("clientchoice")->subscribeEvent(CEGUI::RadioButton::EventSelectStateChanged, CEGUI::Event::Subscriber(&Renderer::handleRadio, this));
  CEGUI::System::getSingleton().renderGUI();

  dynamic_cast<CEGUI::Editbox*>(CEGUI::WindowManager::getSingleton().getWindow("ipaddress"))->setReadOnly(false);
  dynamic_cast<CEGUI::Editbox*>(CEGUI::WindowManager::getSingleton().getWindow("port"))->setReadOnly(false);
  CEGUI::WindowManager::getSingleton().getWindow("port")->setVisible(false);
  dynamic_cast<CEGUI::RadioButton*>(CEGUI::WindowManager::getSingleton().getWindow("serverchoice"))->setSelected(true);
}
bool Renderer:: handleRadio(const CEGUI::EventArgs& e)
{
 const CEGUI::WindowEventArgs &we = static_cast<const CEGUI::WindowEventArgs&>(e);
       CEGUI::RadioButton *radio = static_cast<CEGUI::RadioButton*>(we.window);
       
       std::string buttonName = radio->getName().c_str();

       if( buttonName == "serverchoice" )
       {
         CEGUI::WindowManager::getSingleton().getWindow("ipaddress")->setVisible(false);
         CEGUI::WindowManager::getSingleton().getWindow("ip label")->setVisible(false);
         isserver = true;

       }
       else if ( buttonName == "clientchoice" )
       {
         CEGUI::WindowManager::getSingleton().getWindow("ipaddress")->setVisible(true);
         CEGUI::WindowManager::getSingleton().getWindow("ip label")->setVisible(true);
         isserver = false;
       }

      return true;

}

bool Renderer::handleButton(const CEGUI::EventArgs& e)
{
  
 const CEGUI::WindowEventArgs &we = static_cast<const CEGUI::WindowEventArgs&>(e);
       CEGUI::PushButton *button = static_cast<CEGUI::PushButton*>(we.window);

  std::string buttonName = button->getName().c_str();

  if( buttonName == "Options" )
  {
    CEGUI::System::getSingleton().getGUISheet()->hide();
    CEGUI::System::getSingleton().setGUISheet( myOptions );
    CEGUI::System::getSingleton().getGUISheet()->show();
    CEGUI::System::getSingleton().getGUISheet()->activate();
  }
  else if ( buttonName == "Play" )
  {
    CEGUI::System::getSingleton().getGUISheet()->hide();
    CEGUI::System::getSingleton().setGUISheet( myLoadout );
    CEGUI::System::getSingleton().getGUISheet()->show();
    CEGUI::System::getSingleton().getGUISheet()->activate();
  }
  else if ( buttonName == "Launch" )
  {
    CEGUI::System::getSingleton().getGUISheet()->hide();
    CEGUI::System::getSingleton().setGUISheet( myHUD );
    CEGUI::System::getSingleton().getGUISheet()->show();
    CEGUI::System::getSingleton().getGUISheet()->activate();
    CEGUI::MouseCursor::getSingleton().hide();
    ingame = true;
  }
  else if ( buttonName == "LoadoutBacktomenu" || buttonName == "Backtomenu" )
  {
    CEGUI::System::getSingleton().getGUISheet()->hide();
    CEGUI::System::getSingleton().setGUISheet( myRoot );
    CEGUI::System::getSingleton().getGUISheet()->show();
    CEGUI::System::getSingleton().getGUISheet()->activate();

  }
  else if ( buttonName == "Exit" )
  {
    exit = true;

  }

  return true;
}

void Renderer::drawBox(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4)
{
	glDisable(GL_TEXTURE_2D);
	glLoadIdentity();

	glBegin(GL_QUADS);
		glVertex3f(x1, y1, 0.f);
		glVertex3f(x2, y2, 0.f);
		glVertex3f(x3, y3, 0.f);
		glVertex3f(x4, y4, 0.f);
	glEnd();
	
	glEnable(GL_TEXTURE_2D);
}



void Renderer::setHealth(int currenthealth, int maxhealth )
{


  float health = (float) currenthealth / (float) maxhealth ;
  CEGUI::ProgressBar* pbar = dynamic_cast<CEGUI::ProgressBar*>(CEGUI::WindowManager::getSingleton().getWindow("Health"));
  pbar->setProgress(health);
}

CEGUI::String Renderer::getIP()
{

  CEGUI::Editbox* pbar = dynamic_cast<CEGUI::Editbox*>(CEGUI::WindowManager::getSingleton().getWindow("ipaddress"));

  return pbar->getText();
}

void Renderer::setIP(std::string ipaddressstring)
{

  CEGUI::Editbox* pbar = dynamic_cast<CEGUI::Editbox*>(CEGUI::WindowManager::getSingleton().getWindow("ipaddress"));
  pbar->setText(CEGUI::String(ipaddressstring));

}

void Renderer::setPort(std::string portstring)
{

  CEGUI::Editbox* pbar = dynamic_cast<CEGUI::Editbox*>(CEGUI::WindowManager::getSingleton().getWindow("port"));
  pbar->setText(CEGUI::String(portstring));

}

 CEGUI::String Renderer::getPort()
{

  CEGUI::Editbox* pbar = dynamic_cast<CEGUI::Editbox*>(CEGUI::WindowManager::getSingleton().getWindow("port"));
  return pbar->getText();

}
